<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
        .con {
            border: 1px solid #000000;
            width: 500px;
            height: 600px;
            text-align: center;
            position: relative;
            left: 0;
            right: 0;
            top: 0;
            bottom: 0;
            margin: auto;
        }
 
        .con>div {
            width: 100px;
            height: 100px;
            border: 1px solid #000000;
            box-shadow: 2px 2px 2px #cccccc;
            float: left;
            margin: 11px;
            text-align: center;
            font-size: 40px;
            line-height: 100px;
            user-select: none;
        }
 
        .con>input {
            width: 90%;
            height: 30px;
            line-height: 30px;
            font-size: 25px;
            text-align: right;
            margin-top: 20px;
            margin-bottom: 20px;
            box-shadow: 3px 3px 3px #cccccc;
            outline: none;
            padding: 5px 10px;
            background-color: white;
            border: 1px solid #000000;
        }
 
        .clear::after {
            content: "";
            clear: both;
            display: block;
            height: 0;
            visibility: hidden;
            overflow: hidden;
        }
 
        .clear {
            zoom: 1;
        }
    </style>
</head>
 
<body>
    <div class="con clear">
        <input type="text" id="input" value="0" disabled />
        <div class="divs" id="div1">7</div>
        <div class="divs" id="div2">8</div>
        <div class="divs" id="div3">9</div>
        <div class="divs" id="div4">-</div>
        <div class="divs" id="div5">4</div>
        <div class="divs" id="div6">5</div>
        <div class="divs" id="div7">6</div>
        <div class="divs" id="div8">+</div>
        <div class="divs" id="div9">1</div>
        <div class="divs" id="div10">2</div>
        <div class="divs" id="div11">3</div>
        <div class="divs" id="div12">*</div>
        <div class="divs" id="div13">C</div>
        <div class="divs" id="div14">0</div>
        <div class="divs" id="div15">=</div>
        <div class="divs" id="div16">/</div>
    </div>
    <script>
        var input,
            con,
            result,
            type;
 
        var first = "0", second = "";
        const typeReg = /(\+|\-|\*|\/)$/,
            test = reg => str => reg.test(str),
            typeRegResult = test(typeReg)
 
 
 
        init();
        function init() {
            input = document.querySelector("input");
            con = document.querySelector(".con");
            con.addEventListener("click", clickHandler);
        }
 
 
        function clickHandler(e) {
            if (e.target.className !== "divs") return;
            switch (e.target.innerHTML) {
                case "+":
                case "-":
                case "*":
                case "/":
                    setSymbol(e.target.innerHTML);
                    break;
                case "=":
                    computeResult();
                    break;
                case "C":
                    clear();
                    break;
                default:
                    setValue(e.target.innerHTML);
            }
        }
 
        // 点击 + - * / 时调用
        function setSymbol(_type) {
            /**
             *  目的: 分支成立说明之前计算过, 此时时第二次计算, 所以将之前的计算结果赋值给 first, 然后将计算结果清0
             *          不成立说明此时时第一次计算, 所以将输入框的值直接赋值给 first, 然后将 second 赋值为 0
            */
            if (result) {
                first = result
                result = 0
            } else {
                first = input.value
                second = 0
            }
            type = _type;
            setInputValue();
        }
 
        // 点击 = 计算结果并展示
        function computeResult() {
            result = eval(`${first}${type}${second}`)
            setInputValue();
            second = 0  // 清零后方便后续的二次计算, 不然后因为这个变量有值导致再次计算时渲染内容有误
        }
 
        // 点击 C 清空内容
        function clear() {
            first = "0";
            second = "";
            result = "";
            type = "";
            setInputValue();
        }
 
        // 点击 0~9 时
        function setValue(value) {
            // 点击时如果上一次计算过, 那么先清空一次计算器, 然后在开始正常运行代码
            if (result) {
                clear();
            }
            // 如果 type 没有值, 说明时第一次执行, 那么将值赋值给 first, 如果有值说明之前已经输入过了, 此时可能是 1+ 2- 5* 的情况, 所以将新输入的值赋值给 second
            if (!type) first = Number(first + value) + "";
            else second = Number(second + value) + "";
            setInputValue();
        }
 
        // 拼装内容并展示
        function setInputValue() {
            input.value = first;
            if (!type) return;//真值缩小
            // 目的: 如果目前是 1+2-        这种时候, 再次点击一个运算符时, 将这个最后这个运算符替换为最新的运算符
            if (typeRegResult(input.value)) first = input.value = input.value.slice(0, -1);
            input.value += type;
            if (!second) return;
            input.value += second;
            if (!result) return;
            input.value += "=" + result;
        }
 
        /**
         *  分析流程:
         *          1. 点击 1 + 2 * 3 =
         *          2. 点击 1 + 2 * 3 = 后, 又点击了 + 1 = 
         * 
         *      一、点击 1
         *          1.1 触发 clickHandler 的 default,
         *                  所以调用 setValue 函数, 并将当前标签的文本传入进去此时的值为 1  (也有可能为0~9中的某一个)
         *          1.2 触发 setValue 函数, 因为此时 result 和 type 的值还是 undefined, 
         *              所以此时只会执行第二个if分支, 也就是将 传入的字符赋值给 变量 first
         *              然后此时调用 setInputValue 函数
         *          1.3 调用 setInputValue 函数 因为此时 type 是没有值的, 所以只会将 first 的值赋值给 input.value
         *          1.4 所以此时我们点完之后能看到输入框中的数字变成了1 或者是我们点击的数字
         * 
         *      二、 点击 +
         *          2.1 触发 clickHandler 的 case "+"
         *              所以调用 setSymbol 函数, 并将当前标签的文本传入进去此时的值为 + (也有可能为 + - * /中的某一个)
         *          2.2 调用 setSymbol 函数因为当前 result 的值为 undefined, 所以执行 else 分支
         *              将 输入框的值赋值给 first, 并给 second 赋值为 0, 并且将传入的参数赋值给全局变量 type
         *              重点是此时的 全局变量 type 已经具备值了, 可能是+-*\/中的某一个
         *              然后调用 setInputValue 函数
         *          2.3 调用 setInputValue 函数时首先会将 first 中的值赋值给 input.value
         *              因为 type 此时是有值的, 所以会 if (!type) return 并不会执行
         *              
         *                  然后下边的 if 是为了判断 input.value 这个字符串的末尾是不是 + - * / 这种符号
         *                  如果是的话, 将 first 和 input.value 重新赋值, 也就是将他的最后一个符号删除
         *              然后再拼接上最新的元素符号
         * 
         *              接下来的判断 因为 second 目前没有值, 所以不会继续往下运行
         * 
         *          2.4 所以此时我们点完之后能看到输入框中的数字变成了  1+      或者是你点击的数字与符号的拼接
         * 
         * 
         *      三、点击 2
         *          3.1 触发 clickHandler 的 default,
         *                  所以调用 setValue 函数, 并将当前标签的文本传入进去此时的值为 2  (也有可能为0~9中的某一个)
         *          3.2 触发 setValue 函数, 此时 result 的值还是 undefined 所以不会执行,
         *              但是 type 此时已经有值了, 就是之前点击的符号, 所以此时会运行 else 分支, 也就是将 传入的字符赋值给 变量 second
         *              然后此时调用 setInputValue 函数
         *          3.3 调用 setInputValue 函数
         *                  3.3.1 首先会将 first 的值 赋值给 input.value
         *                  3.3.2 然后将因为 type 有值会继续向下运行, 所以将符号拼接到了 input.value 上
         *                  3.3.3 因为 此时 second 也有值了, 目前案例中是 2 但是也有可能是(0~9中的某一个), 所以此时又将第二次输入的值拼接到了展示框中
         *          3.4 所以此时我们点完之后能看到输入框中的数字变成了1+2 或者是我们点击的数字与符号的组合
         * 
         *      四、点击 *
         *          4.1 触发 clickHandler 的 case "*"
         *              所以调用 setSymbol 函数, 并将当前标签的文本传入进去此时的值为 * (也有可能为 + - * /中的某一个)
         *          4.2 调用 setSymbol 函数因为当前 result 的值为 undefined, 所以执行 else 分支
         *              将 输入框的值赋值给 first, 并给 second 赋值为 0, 并且将传入的参数赋值给全局变量 type
         *              重点是此时的 全局变量 type 已经具备值了, 可能是+-*\/中的某一个
         *              然后调用 setInputValue 函数
         *          4.3 调用 setInputValue 函数时首先会将 first 中的值赋值给 input.value  (1+2)
         *              因为 type 此时是有值的, 所以会 if (!type) return 并不会执行
         *              所以会将此时又点击的符号, 拼接到 input.value 后
         *              因为 second 再 4.2 时已经被我们修改为 0 了, 所以此时 if(!second) return 条件成立, 功能到此结束
         *          4.4 所以此时我们点完之后能看到输入框中的数字变成了  1+2*      或者是你点击的数字与符号的拼接
         * 
         *      五、点击 3
         *          5.1 触发 clickHandler 的 default,
         *                  所以调用 setValue 函数, 并将当前标签的文本传入进去此时的值为 3  (也有可能为0~9中的某一个)
         *          5.2 触发 setValue 函数, 此时 result 的值还是 undefined 所以不会执行,
         *              但是 type 此时已经有值了, 就是之前点击的符号, 所以此时会运行 else 分支, 也就是将 传入的字符赋值给 变量 second
         *              然后此时调用 setInputValue 函数
         *          5.3 调用 setInputValue 函数
         *                  5.3.1 首先会将 first 的值 赋值给 input.value (1+2)
         *                  5.3.2 然后将因为 type 有值会继续向下运行, 所以将符号拼接到了 input.value 上 (1+2*)
         *                  5.3.3 因为 此时 second 也有值了, 目前案例中是 3 但是也有可能是(0~9中的某一个), 所以此时又将第二次输入的值拼接到了展示框中 (1+2*3)
         *          5.4 所以此时我们点完之后能看到输入框中的数字变成了1+2*3 或者是我们点击的数字与符号的组合
         * 
         *      六、点击 =
         *          6.1 触发 clickHandler 的 case "="
         *                  所以调用 computeResult()
         *          6.2 调用 computeResult() 时
         *              利用 first---type---second 三个变量拼装了一个结算等式 (1+2*3)
         *              然后利用 eval 函数解算出结果, 并赋值给 result
         *              计算完毕后调用 setInputValue 函数
         *              
         *              6.2.1 首先会将 first 的值 赋值给 input.value (1+2)
         *              6.2.2 然后将因为 type 有值会继续向下运行, 所以将符号拼接到了 input.value 上 (1+2*)
         *              6.2.3 因为 此时 second 也有值了, 目前案例中是 3 但是也有可能是(0~9中的某一个), 所以此时又将第二次输入的值拼接到了展示框中 (1+2*3)
         *              6.2.4 因为 调用该函数之前, result 已经有值了, 所以会将 input.value 上又拼接了一个 = 与 result
         *                          也就是 1+2*3=7
         *          6.3 将 second 重新赋值为 0
         * 
         * 
         *      七、点击 + (此时为之前已经计算过一次后的点击)
         *          7.1 触发 clickHandler 的 case "+"
         *              所以调用 setSymbol 函数, 并将当前标签的文本传入进去此时的值为 + (也有可能为 + - * /中的某一个)
         *          7.2 调用 setSymbol 函数因为当前 result 的值为 上一次的计算结果, 所以执行 if 分支 而不是 else 分支
         *              将 上一次的计算结果赋值给 first, 并给 result 赋值为 0, 然后将传入的参数赋值给全局变量 type
         *              重点是此时的 全局变量 type 已经具备值了, 可能是+-*\/中的某一个
         *              然后调用 setInputValue 函数
         *          7.3 调用 setInputValue 函数时首先会将 first 中的值赋值给 input.value  (7)
         *              因为 type 此时是有值的, 所以会 if (!type) return 并不会执行
         *              所以会将此时又点击的符号, 拼接到 input.value 后     (7+)
         *              因为 second 再 6.3 时已经被我们修改为 0 了, 所以此时 if(!second) return 条件成立, 功能到此结束
         *          7.4 所以此时我们点完之后能看到输入框中的数字变成了  7+      或者是你点击的数字与符号的拼接
         * 
         *      八、点击 1
         *          8.1 触发 clickHandler 的 default,
         *                  所以调用 setValue 函数, 并将当前标签的文本传入进去此时的值为 1  (也有可能为0~9中的某一个)
         *          8.2 触发 setValue 函数, 因为此时 result 的值再7.2 变成了0 但是 type 有值, 所以执行 else 分支
         *              所以此时只会执行第else分支, 也就是将 传入的字符赋值给 变量 second
         *              然后此时调用 setInputValue 函数
         *          8.3 调用 setInputValue 函数
         *                  首先将 first 的值赋值给 输入框 (7)
         *                  然后将符号拼接上去 (7+)
         *                  然后将又点击的数字拼接上去 (7+1)
         *          8.4 所以此时我们点完之后能看到输入框中的数字变成了 7+1 或者是我们点击的数字
         * 
         *      九、点击 =
         *          6.1 触发 clickHandler 的 case "="
         *                  所以调用 computeResult()
         *          6.2 调用 computeResult() 时
         *              利用 first---type---second 三个变量拼装了一个结算等式 (7+1)
         *              然后利用 eval 函数解算出结果, 并赋值给 result
         *              计算完毕后调用 setInputValue 函数
         *              
         *              7.2.1 首先会将 first 的值 赋值给 input.value (7)
         *              7.2.2 然后将因为 type 有值会继续向下运行, 所以将符号拼接到了 input.value 上 (7+)
         *              7.2.3 因为 此时 second 也有值了, 目前案例中是 1 但是也有可能是(0~9中的某一个), 所以此时又将第二次输入的值拼接到了展示框中 (7+1)
         *              7.2.4 因为 调用该函数之前, result 已经有值了, 所以会将 input.value 上又拼接了一个 = 与 result
         *                          也就是 7+1=8
         *          6.3 将 second 重新赋值为 0
        */
    </script>
</body>
 
</html>

